package com.file.parser;

import com.file.bo.ResponseData;
import com.file.bo.mail.ICBCCorp;
import com.file.bo.mail.ICBCCorpSummary;
import com.file.bo.mail.ICBCCorpTran;
import com.file.constant.ErrorCode;
import com.file.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import technology.tabula.*;
import technology.tabula.detectors.NurminenDetectionAlgorithm;
import technology.tabula.extractors.SpreadsheetExtractionAlgorithm;

import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 商银行企业版pdf流水解析
 * @author anyspa
 */

@Slf4j
public class ICBCCorpPdfParser extends BasePdfParser {

    public ResponseData<String> parseICBCCorpPdfToJson(String daId, String filePath) {
        log.info("parseICBCCorpPdfToJson started, daId:{}", daId);
        String json = null;

        try {
            ICBCCorp icbcCorp = parseICBCCorpPdf(filePath);
            json = JsonUtils.convertObjectToJson(icbcCorp);
        } catch (Exception e) {
            log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), daId, "", "", "parseICBCCorpPdfToJson failed", e);
            return new ResponseData<String>(json, ErrorCode.FILE_PARSE_EXCEPTION.getCode(), ErrorCode.FILE_PARSE_EXCEPTION.getMsg());
        }

        log.info("parseICBCCorpPdfToJson completed, daId:{}", daId);
        return new ResponseData<String>(json, ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg());
    }

    public ICBCCorp parseICBCCorpPdf(String filePath) {
        ICBCCorp icbcCorp = parseICBCCorpHeader(filePath);
        List<ICBCCorpTran> icbcCorpTrans = parseTrans(filePath);
        icbcCorp.setIcbcCorpTrans(icbcCorpTrans);
        ICBCCorpSummary icbcCorpSummary = analyzeData(icbcCorpTrans, icbcCorp.getTimeRange(), icbcCorp.getAccountNo(), icbcCorp.getAccountName());
        icbcCorp.setIcbcCorpSummary(icbcCorpSummary);
        return icbcCorp;
    }

    public ICBCCorp parseICBCCorpHeader(String filePath) {
        ICBCCorp icbcCorp = new ICBCCorp();
        String pdfHeaderText = parsePdfHeaderText(filePath);
        String accountNo = pdfHeaderText.substring(pdfHeaderText.indexOf("账号：") + 3, pdfHeaderText.indexOf("币种：")).trim();
        String currency = pdfHeaderText.substring(pdfHeaderText.indexOf("币种：") + 3, pdfHeaderText.indexOf("单位：")).trim();
        String unit = pdfHeaderText.substring(pdfHeaderText.indexOf("单位：") + 3, pdfHeaderText.indexOf("本方账号户名：")).trim();
        String accountName = pdfHeaderText.substring(pdfHeaderText.indexOf("本方账号户名：") + 7, pdfHeaderText.indexOf("本方账号开户行：")).trim();
        String accountBank = pdfHeaderText.substring(pdfHeaderText.indexOf("本方账号开户行：") + 8, pdfHeaderText.indexOf("时间范围：")).trim();
        String timeRange = null;
        if (pdfHeaderText.contains("交易时间")) {
            timeRange = pdfHeaderText.substring(pdfHeaderText.indexOf("时间范围：") + 5, pdfHeaderText.indexOf("交易时间")).trim();
        } else {
            timeRange = pdfHeaderText.substring(pdfHeaderText.indexOf("时间范围：") + 5).trim();
        }
        icbcCorp.setAccountNo(accountNo);
        icbcCorp.setCurrency(currency);
        icbcCorp.setUnit(unit);
        icbcCorp.setAccountName(accountName);
        icbcCorp.setAccountBank(accountBank);
        icbcCorp.setTimeRange(timeRange);
        return icbcCorp;
    }

    public List<ICBCCorpTran> parseTrans(String filePath) {
        List<ICBCCorpTran> icbcCorpTrans = new ArrayList<>();

        // 1. 读取文件
        File pdf = new File(filePath);

        // 2. pdfbox读取PDDocument
        try (PDDocument pdfDocument = PDDocument.load(pdf)) {

            // 3. tabula新建ObjectExtractor和NurminenDetectionAlgorithm，同时准备接收表格Rectangle的结构
            ObjectExtractor objectExtractor = new ObjectExtractor(pdfDocument);
            NurminenDetectionAlgorithm detectionAlgorithm = new NurminenDetectionAlgorithm();
            Map<Integer, List<Rectangle>> detectedTables = new HashMap<>();

            // 4. 获取每页的PageIterator
            PageIterator pages = objectExtractor.extract();

            // 5. 解析每页的Rectangle(table的位置)
            while (pages.hasNext()) {
                Page page = pages.next();
                List<Rectangle> tablesOnPage = detectionAlgorithm.detect(page);
                if (tablesOnPage.size() > 0) {
                    detectedTables.put(Integer.valueOf(page.getPageNumber()), tablesOnPage);
                }
            }

            // 6.通过table位置获取表格具体内容
            SpreadsheetExtractionAlgorithm bea = new SpreadsheetExtractionAlgorithm();

            String excludeColumn = null;

            // 如果单页pdf中有多个表格，则解析出来的List<Rectangle>会有多个对象
            for (Map.Entry<Integer, List<Rectangle>> entry : detectedTables.entrySet()) {
                Page page = objectExtractor.extract(entry.getKey());

                Rectangle rectangle = entry.getValue().get(0);
                Page area = page.getArea(rectangle.getTop(), rectangle.getLeft(), rectangle.getBottom(), rectangle.getRight());

                List<Table> table = bea.extract(area);

                Table t = table.get(0);

                if (t.getColCount() != 12) {
                    log.error("OnError|{}||{}|{}|{}|{}", ErrorCode.FILE_PARSE_EXCEPTION.getCode(), "", "", "", "ICBCCorp Pdf format changed");
                    throw new RuntimeException();
                }

                // 解析第一页时，去掉表头两行数据
//                if (entry.getKey() == 1) {
                    for (int i = 0; i < t.getRowCount(); i++) {
                        if (t.getCell(i, 0).getText(false).contains("交易时间") || t.getCell(i, 0).getText(false).contains("本方账号户名")) {
                            continue;
                        }
                        ICBCCorpTran icbcCorpTran = getTransactionByTable(t, i);
                        icbcCorpTrans.add(icbcCorpTran);
                    }
//                } else {
//                    for (int i = 0; i < t.getRowCount(); i++) {
//                        ICBCCorpTran icbcCorpTran = getTransactionByTable(t, i);
//                        icbcCorpTrans.add(icbcCorpTran);
//                    }
//                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return icbcCorpTrans;
    }

    public ICBCCorpSummary analyzeData(List<ICBCCorpTran> icbcCorpTrans, String timeRange, String accountNo, String accountName) {
        ICBCCorpSummary icbcCorpSummary = new ICBCCorpSummary();
        BigDecimal totalDebitAmt = new BigDecimal("0.00");
        Integer totalDebitNo = 0;
        BigDecimal recentThirtyDaysTotalDebitAmt = new BigDecimal("0.00");
        Integer recentThirtyDaysTotalDebitNo = 0;


        String startDateStr = timeRange.substring(0, 8);
        String endDateStr = timeRange.substring(timeRange.length()-8);
        String recentThirtyDaysEndDateStr = null;
        if (CollectionUtils.isNotEmpty(icbcCorpTrans)) {
            recentThirtyDaysEndDateStr = icbcCorpTrans.get(0).getTranTime().substring(0, 8);
        } else {
            recentThirtyDaysEndDateStr = endDateStr;
        }

        String recentThirtyDaysStartDateStr = caculateRecentThirtyDaysStartDateStr(startDateStr, recentThirtyDaysEndDateStr);

        try {
            for (ICBCCorpTran icbcCorpTran : icbcCorpTrans) {
                if (StringUtils.isNotBlank(icbcCorpTran.getDebitAmt())) {
                    BigDecimal debitAmt = new BigDecimal(icbcCorpTran.getDebitAmt().replace(",", ""));

                    totalDebitAmt = totalDebitAmt.add(debitAmt);
                    totalDebitNo++;

                    String tranDateStr = icbcCorpTran.getTranTime().substring(0, 8);
                    LocalDate tranDate = LocalDate.parse(tranDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
                    LocalDate recentThirtyDaysStartDate = LocalDate.parse(recentThirtyDaysStartDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
                    if (!tranDate.isBefore(recentThirtyDaysStartDate)) {
                        recentThirtyDaysTotalDebitAmt = recentThirtyDaysTotalDebitAmt.add(debitAmt);
                        recentThirtyDaysTotalDebitNo++;
                    }
                }
            }
        } catch (Exception e) {
            log.info("ICBCCorp dataAnalyze failed", e);
        }

        icbcCorpSummary.setCompanyName(accountName);
        icbcCorpSummary.setAccountBank("中国工商银行");
        icbcCorpSummary.setAccountNo(accountNo);
        icbcCorpSummary.setStartDate(startDateStr);
        icbcCorpSummary.setEndDate(endDateStr);
        icbcCorpSummary.setTotalDebitAmt(String.valueOf(totalDebitAmt));
        icbcCorpSummary.setTotalDebitNo(String.valueOf(totalDebitNo));
        icbcCorpSummary.setRecentThirtyDaysStartDate(recentThirtyDaysStartDateStr);
        icbcCorpSummary.setRecentThirtyDaysEndDate(recentThirtyDaysEndDateStr);
        icbcCorpSummary.setRecentThirtyDaysTotalDebitAmt(String.valueOf(recentThirtyDaysTotalDebitAmt));
        icbcCorpSummary.setRecentThirtyDaysTotalDebitNo(String.valueOf(recentThirtyDaysTotalDebitNo));

        return icbcCorpSummary;
    }

    private String caculateRecentThirtyDaysStartDateStr(String startDateStr, String endDateStr) {
        LocalDate endDate = LocalDate.parse(endDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
        LocalDate startDate = LocalDate.parse(startDateStr, DateTimeFormatter.ofPattern("yyyyMMdd"));
        LocalDate calStartDate = endDate.minusDays(29L);
        LocalDate finalStartDate = startDate.isAfter(calStartDate)? startDate : calStartDate;
        return DateTimeFormatter.ofPattern("yyyyMMdd").format(finalStartDate);
    }

    private ICBCCorpTran getTransactionByTable(Table t, int i) {
        ICBCCorpTran icbcCorpTran = new ICBCCorpTran();

        icbcCorpTran.setTranTime(t.getCell(i, 0).getText(false));
        icbcCorpTran.setAccountNo(t.getCell(i, 1).getText(false));
        icbcCorpTran.setCounterpartyAccountName(t.getCell(i, 2).getText(false));
        icbcCorpTran.setCounterpartyAccountNo(t.getCell(i, 3).getText(false));
        icbcCorpTran.setCounterpartyBank(t.getCell(i, 4).getText(false));
        icbcCorpTran.setVoucherNo(t.getCell(i, 5).getText(false));
        icbcCorpTran.setDebitCredit(t.getCell(i, 6).getText(false));
        icbcCorpTran.setDebitAmt(t.getCell(i, 7).getText(false));
        icbcCorpTran.setCreditAmt(t.getCell(i, 8).getText(false));
        icbcCorpTran.setSummary(t.getCell(i, 9).getText(false));
        icbcCorpTran.setPurpose(t.getCell(i, 10).getText(false));
        icbcCorpTran.setBalance(t.getCell(i, 11).getText(false));

        return icbcCorpTran;
    }

    public String getAccountName(String filePath) {
        String pdfHeaderText = parsePdfHeaderText(filePath);
        String accountName = pdfHeaderText.substring(pdfHeaderText.indexOf("本方账号户名：") + 7, pdfHeaderText.indexOf("本方账号开户行：")).trim();
        return accountName;
    }

    public static void main(String[] args) {
        ICBCCorpPdfParser icbcCorpPdfParser = new ICBCCorpPdfParser();
//        ICBCCorp icbcCorp = icbcCorpPdfParser.parseICBCCorpPdf("D:\\data\\files\\ICBCCorp\\historydetail28.pdf");
        ICBCCorp icbcCorp = icbcCorpPdfParser.parseICBCCorpPdf("D:\\data\\files\\ICBCCorp\\historydetail0.pdf");
        System.out.println(JsonUtils.convertObjectToJson(icbcCorp));
    }

}
